!=========================================================================
!
! Shared thermo variables, subroutines
!
! authors: Elizabeth C. Hunke, LANL

      module icepack_therm_shared

      use icepack_kinds

      use icepack_parameters, only: c0, c1, c2, c4, p5, pi
      use icepack_parameters, only: cp_ocn, cp_ice, rhoi, rhos, Tffresh, TTTice, qqqice
      use icepack_parameters, only: stefan_boltzmann, emissivity, Lfresh, Tsmelt
      use icepack_parameters, only: saltmax, min_salin, depressT
      use icepack_parameters, only: ktherm, heat_capacity, tfrz_option
      use icepack_parameters, only: calc_Tsfc
      use icepack_warnings, only: warnstr, icepack_warnings_add
      use icepack_warnings, only: icepack_warnings_setabort, icepack_warnings_aborted

      use icepack_mushy_physics, only: enthalpy_mush
      use icepack_mushy_physics, only: temperature_snow
      use icepack_mushy_physics, only: enthalpy_snow
      use icepack_mushy_physics, only: icepack_mushy_temperature_mush
      use icepack_mushy_physics, only: liquidus_temperature_mush
    
      implicit none

      private
      public :: calculate_Tin_from_qin, &
                surface_heat_flux, &
                dsurface_heat_flux_dTsf, &
                icepack_init_thermo, &
                icepack_init_trcr, &
                icepack_ice_temperature, &
                icepack_snow_temperature, &
                icepack_liquidus_temperature, &
                icepack_sea_freezing_temperature, &
                icepack_enthalpy_snow

      real (kind=dbl_kind), parameter, public :: &
         ferrmax = 1.0e-3_dbl_kind    ! max allowed energy flux error (W m-2)
                                      ! recommend ferrmax < 0.01 W m-2

      real (kind=dbl_kind), parameter, public :: &
         Tmin = -100.0_dbl_kind ! min allowed internal temperature (deg C)

      logical (kind=log_kind), public :: &
         l_brine         ! if true, treat brine pocket effects

      real (kind=dbl_kind), parameter, public :: &
         hfrazilmin = 0.05_dbl_kind ! min thickness of new frazil ice (m)

      real (kind=dbl_kind), public :: &
         hi_min          ! minimum ice thickness allowed (m)

!=======================================================================

      contains

!=======================================================================
!
!  Compute the internal ice temperatures from enthalpy using
!  quadratic formula

      function calculate_Tin_from_qin (qin, Tmltk) &
               result(Tin)

      real (kind=dbl_kind), intent(in) :: &
         qin   , &              ! enthalpy
         Tmltk                  ! melting temperature at one level

      real (kind=dbl_kind) :: &
         Tin                 ! internal temperature

      ! local variables

      real (kind=dbl_kind) :: &
         aa1,bb1,cc1         ! quadratic solvers

      character(len=*),parameter :: subname='(calculate_Tin_from_qin)'

      if (l_brine) then
         aa1 = cp_ice
         bb1 = (cp_ocn-cp_ice)*Tmltk - qin/rhoi - Lfresh 
         cc1 = Lfresh * Tmltk
         Tin =  min((-bb1 - sqrt(bb1*bb1 - c4*aa1*cc1)) /  &
                         (c2*aa1),Tmltk)

      else                ! fresh ice
         Tin = (Lfresh + qin/rhoi) / cp_ice
      endif
 
      end function calculate_Tin_from_qin

!=======================================================================
! Surface heat flux
!=======================================================================

! heat flux into ice
    
      subroutine surface_heat_flux(Tsf,     fswsfc, &
                                   rhoa,    flw,    &
                                   potT,    Qa,     &
                                   shcoef,  lhcoef, &
                                   flwoutn, fsensn, &
                                   flatn,   fsurfn)

      ! input surface temperature
      real(kind=dbl_kind), intent(in) :: &
         Tsf             ! ice/snow surface temperature (C)
    
      ! input variables
      real(kind=dbl_kind), intent(in) :: &
         fswsfc      , & ! SW absorbed at ice/snow surface (W m-2)
         rhoa        , & ! air density (kg/m^3)
         flw         , & ! incoming longwave radiation (W/m^2)
         potT        , & ! air potential temperature  (K)
         Qa          , & ! specific humidity (kg/kg)
         shcoef      , & ! transfer coefficient for sensible heat
         lhcoef          ! transfer coefficient for latent heat
    
      ! output
      real(kind=dbl_kind), intent(out) :: &
         fsensn      , & ! surface downward sensible heat (W m-2)
         flatn       , & ! surface downward latent heat (W m-2)
         flwoutn     , & ! upward LW at surface (W m-2)
         fsurfn          ! net flux to top surface, excluding fcondtopn
    
      ! local variables
      real(kind=dbl_kind) :: &
         TsfK        , & ! ice/snow surface temperature (K)
         Qsfc        , & ! saturated surface specific humidity (kg/kg)
         qsat        , & ! the saturation humidity of air (kg/m^3)
         flwdabs     , & ! downward longwave absorbed heat flx (W/m^2)
         tmpvar          ! 1/TsfK
    
      character(len=*),parameter :: subname='(surface_heat_flux)'

      ! ice surface temperature in Kelvin
      TsfK = Tsf + Tffresh
!      TsfK = max(Tsf + Tffresh, c1)
      tmpvar = c1/TsfK
    
      ! saturation humidity
      qsat    = qqqice * exp(-TTTice*tmpvar)
      Qsfc    = qsat / rhoa
    
      ! longwave radiative flux
      flwdabs =  emissivity * flw
      flwoutn = -emissivity * stefan_boltzmann * TsfK**4
    
      ! downward latent and sensible heat fluxes
      fsensn = shcoef * (potT - TsfK)
      flatn  = lhcoef * (Qa - Qsfc)
    
      ! combine fluxes
      fsurfn = fswsfc + flwdabs + flwoutn + fsensn + flatn

      end subroutine surface_heat_flux

  !=======================================================================
  
      subroutine dsurface_heat_flux_dTsf(Tsf,  rhoa,      &
                                         shcoef,  lhcoef, &
                                         dfsurfn_dTsf, dflwoutn_dTsf, &
                                         dfsensn_dTsf, dflatn_dTsf)
    
      ! input surface temperature
      real(kind=dbl_kind), intent(in) :: &
         Tsf               ! ice/snow surface temperature (C)
    
      ! input variables
      real(kind=dbl_kind), intent(in) :: &
         rhoa          , & ! air density (kg/m^3)
         shcoef        , & ! transfer coefficient for sensible heat
         lhcoef            ! transfer coefficient for latent heat
    
      ! output
      real(kind=dbl_kind), intent(out) :: &
         dfsurfn_dTsf      ! derivative of net flux to top surface, excluding fcondtopn
    
      real(kind=dbl_kind), intent(out) :: &
         dflwoutn_dTsf , & ! derivative of longwave flux wrt surface temperature
         dfsensn_dTsf  , & ! derivative of sensible heat flux wrt surface temperature
         dflatn_dTsf       ! derivative of latent heat flux wrt surface temperature
    
      ! local variables
      real(kind=dbl_kind) :: &
         TsfK          , & ! ice/snow surface temperature (K)
         dQsfc_dTsf    , & ! saturated surface specific humidity (kg/kg)
         qsat          , & ! the saturation humidity of air (kg/m^3)
         tmpvar            ! 1/TsfK
    
      character(len=*),parameter :: subname='(dsurface_heat_flux_dTsf)'

      ! ice surface temperature in Kelvin
!      TsfK = max(Tsf + Tffresh, c1)
      TsfK = Tsf + Tffresh
      tmpvar = c1/TsfK
    
      ! saturation humidity
      qsat          = qqqice * exp(-TTTice*tmpvar)
      dQsfc_dTsf    = TTTice * tmpvar * tmpvar * (qsat / rhoa)
    
      ! longwave radiative flux
      dflwoutn_dTsf = -emissivity * stefan_boltzmann * c4*TsfK**3
    
      ! downward latent and sensible heat fluxes
      dfsensn_dTsf = -shcoef
      dflatn_dTsf  = -lhcoef * dQsfc_dTsf
    
      ! combine fluxes
      dfsurfn_dTsf = dflwoutn_dTsf + dfsensn_dTsf + dflatn_dTsf
    
      end subroutine dsurface_heat_flux_dTsf

!=======================================================================
!autodocument_start icepack_init_thermo
! Initialize the vertical profile of ice salinity and melting temperature.
!
! authors: C. M. Bitz, UW
!          William H. Lipscomb, LANL

      subroutine icepack_init_thermo(nilyr, sprofile)

      integer (kind=int_kind), intent(in) :: &
         nilyr                            ! number of ice layers

      real (kind=dbl_kind), dimension(:), intent(out) :: &
         sprofile                         ! vertical salinity profile

!autodocument_end

      real (kind=dbl_kind), parameter :: &
         nsal    = 0.407_dbl_kind, &
         msal    = 0.573_dbl_kind

      integer (kind=int_kind) :: k        ! ice layer index
      real (kind=dbl_kind)    :: zn       ! normalized ice thickness

      character(len=*),parameter :: subname='(icepack_init_thermo)'

      !-----------------------------------------------------------------
      ! Determine l_brine based on saltmax.
      ! Thermodynamic solver will not converge if l_brine is true and
      !  saltmax is close to zero.
      ! Set l_brine to false for zero layer thermodynamics
      !-----------------------------------------------------------------

      heat_capacity = .true.      
      if (ktherm == 0) heat_capacity = .false. ! 0-layer thermodynamics

      l_brine = .false.
      if (saltmax > min_salin .and. heat_capacity) l_brine = .true.

      !-----------------------------------------------------------------
      ! Prescibe vertical profile of salinity and melting temperature.
      ! Note this profile is only used for BL99 thermodynamics.
      !-----------------------------------------------------------------

      if (l_brine) then
         do k = 1, nilyr
            zn = (real(k,kind=dbl_kind)-p5) /  &
                  real(nilyr,kind=dbl_kind)
            sprofile(k)=(saltmax/c2)*(c1-cos(pi*zn**(nsal/(msal+zn))))
            sprofile(k) = max(sprofile(k), min_salin)
         enddo ! k
         sprofile(nilyr+1) = saltmax

      else ! .not. l_brine
         do k = 1, nilyr+1
            sprofile(k) = c0
         enddo
      endif ! l_brine

      end subroutine icepack_init_thermo

!=======================================================================
!autodocument_start icepack_init_trcr
!
      subroutine icepack_init_trcr(Tair,     Tf,       &
                                  Sprofile, Tprofile, &
                                  Tsfc,               &
                                  nilyr,    nslyr,    &
                                  qin,      qsn)

      integer (kind=int_kind), intent(in) :: &
         nilyr, &    ! number of ice layers
         nslyr       ! number of snow layers

      real (kind=dbl_kind), intent(in) :: &
         Tair, &     ! air temperature (C)
         Tf          ! freezing temperature (C)

      real (kind=dbl_kind), dimension(:), intent(in) :: &
         Sprofile, & ! vertical salinity profile (ppt)
         Tprofile    ! vertical temperature profile (C)

      real (kind=dbl_kind), intent(out) :: &
         Tsfc        ! surface temperature (C)

      real (kind=dbl_kind), dimension(:), intent(out) :: &
         qin, &      ! ice enthalpy profile (J/m3)
         qsn         ! snow enthalpy profile (J/m3)

!autodocument_end

      ! local variables

      integer (kind=int_kind) :: k

      real (kind=dbl_kind) :: &
         slope, Ti

      character(len=*),parameter :: subname='(icepack_init_trcr)'

      ! surface temperature
      Tsfc = Tf ! default
      if (calc_Tsfc) Tsfc = min(Tsmelt, Tair - Tffresh) ! deg C
      
      if (heat_capacity) then
        
        ! ice enthalpy
        do k = 1, nilyr
          ! assume linear temp profile and compute enthalpy
          slope = Tf - Tsfc
          Ti = Tsfc + slope*(real(k,kind=dbl_kind)-p5) &
              /real(nilyr,kind=dbl_kind)
          if (ktherm == 2) then
            qin(k) = enthalpy_mush(Ti, Sprofile(k))
          else
            qin(k) = -(rhoi * (cp_ice*(Tprofile(k)-Ti) &
                + Lfresh*(c1-Tprofile(k)/Ti) - cp_ocn*Tprofile(k)))
          endif
        enddo               ! nilyr
        
        ! snow enthalpy
        do k = 1, nslyr
          Ti = min(c0, Tsfc)
          qsn(k) = -rhos*(Lfresh - cp_ice*Ti)
        enddo               ! nslyr
        
      else  ! one layer with zero heat capacity
        
        ! ice energy
        qin(1) = -rhoi * Lfresh 
        
        ! snow energy
        qsn(1) = -rhos * Lfresh 
        
      endif               ! heat_capacity
      
    end subroutine icepack_init_trcr

!=======================================================================
!autodocument_start icepack_liquidus_temperature
! compute liquidus temperature

      function icepack_liquidus_temperature(Sin) result(Tmlt)

        real(dbl_kind), intent(in) :: Sin
        real(dbl_kind) :: Tmlt

!autodocument_end

        character(len=*),parameter :: subname='(icepack_liquidus_temperature)'

        if (ktherm == 2) then

           Tmlt = liquidus_temperature_mush(Sin)

        else

           Tmlt = -depressT * Sin

        endif

      end function icepack_liquidus_temperature

!=======================================================================
!autodocument_start icepack_sea_freezing_temperature
! compute ocean freezing temperature

      function icepack_sea_freezing_temperature(sss) result(Tf)

        real(dbl_kind), intent(in) :: sss
        real(dbl_kind) :: Tf

!autodocument_end

        character(len=*),parameter :: subname='(icepack_sea_freezing_temperature)'

        if (trim(tfrz_option) == 'mushy') then

           Tf = icepack_liquidus_temperature(sss) ! deg C
           
        elseif (trim(tfrz_option) == 'linear_salt') then

           Tf = -depressT * sss ! deg C

        else

           Tf = -1.8_dbl_kind

        endif

      end function icepack_sea_freezing_temperature

!=======================================================================
!autodocument_start icepack_ice_temperature
! compute ice temperature

      function icepack_ice_temperature(qin, Sin) result(Tin)

        real(kind=dbl_kind), intent(in) :: qin, Sin
        real(kind=dbl_kind) :: Tin

!autodocument_end

        real(kind=dbl_kind) :: Tmlts

        character(len=*),parameter :: subname='(icepack_ice_temperature)'

        if (ktherm == 2) then

           Tin = icepack_mushy_temperature_mush(qin, Sin)

        else

           Tmlts = -depressT * Sin
           Tin = calculate_Tin_from_qin(qin,Tmlts)

        endif

      end function icepack_ice_temperature

!=======================================================================
!autodocument_start icepack_snow_temperature
! compute snow temperature

      function icepack_snow_temperature(qin) result(Tsn)

        real(kind=dbl_kind), intent(in) :: qin
        real(kind=dbl_kind) :: Tsn

!autodocument_end

        character(len=*),parameter :: subname='(icepack_snow_temperature)'

        if (ktherm == 2) then

           Tsn = temperature_snow(qin)

        else

           Tsn = (Lfresh + qin/rhos)/cp_ice

        endif

      end function icepack_snow_temperature

!=======================================================================
!autodocument_start icepack_enthalpy_snow
! compute snow enthalpy

      function icepack_enthalpy_snow(zTsn) result(qsn)

        real(kind=dbl_kind), intent(in) :: zTsn
        real(kind=dbl_kind) :: qsn

!autodocument_end

        character(len=*),parameter :: subname='(icepack_enthalpy_snow)'

        qsn = enthalpy_snow(zTsn)

      end function icepack_enthalpy_snow

!=======================================================================

      end module icepack_therm_shared

!=======================================================================
